package edu.ntu.cloudroid.xacml.obligation;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


import com.sun.xacml.Obligation;
import com.sun.xacml.attr.IntegerAttribute;
import com.sun.xacml.attr.StringAttribute;
import com.sun.xacml.ctx.Attribute;
import com.sun.xacml.ctx.RequestCtx;

import edu.ntu.cloudroid.model.data.AbstractData;
import edu.ntu.cloudroid.model.data.DetachedData;
import edu.ntu.cloudroid.model.data.VitalSignData;
import edu.ntu.cloudroid.model.data.VitalSignInfo;
import edu.ntu.cloudroid.model.policy.SimpleFiltering;
import edu.ntu.cloudroid.model.profile.ProfileData;
import edu.ntu.cloudroid.server.RequestServlet;
import edu.ntu.cloudroid.server.backendservice.DataService;
import edu.ntu.cloudroid.server.backendservice.FriendService;
import edu.ntu.cloudroid.xacml.Utilities;
import edu.ntu.cloudroid.xacml.XACMLException;
import edu.ntu.cloudroid.xacml.XACMLStrings;

/**
 * Simple summary policy. Retrieve all the data from the last 
 * 
 */
public class FilteringObligation implements ObligationHandle {

	@Override
	public List<DetachedData> processObligation(ProfileData subject, ProfileData resource,
			RequestCtx requestCtx, Obligation o) throws Exception {
		List<Attribute> assignments = o.getAssignments();
		int cond = 0;
		String val = "";
		int colIdx = 0;	
		boolean latestOnly = false;
		for (Attribute a : assignments) {
			if (a.getId().equals(new URI(XACMLStrings.ATTR_Filtering_Cond))) {
				IntegerAttribute attr = (IntegerAttribute) a.getValue();
				cond = (int) attr.getValue();
			}
			if (a.getId().equals(new URI(XACMLStrings.ATTR_Filtering_ColumnIdx))) {
				IntegerAttribute attr = (IntegerAttribute) a.getValue();
				colIdx = (int) attr.getValue();
			}
			else if (a.getId().equals(
					new URI(XACMLStrings.ATTR_Filtering_Val))) {
				StringAttribute sa = (StringAttribute) a.getValue();
				val = sa.getValue();
			}
			else if (a.getId().equals(new URI(XACMLStrings.ATTR_GetLatest)))
				latestOnly = true;
		}
		return this.evaluateFilteringCondition(subject, resource, cond, val,
				latestOnly, colIdx, resource.getDataId());
	}

	/**
	 * Different obligation handler for different type of data MUST extend this
	 * 
	 * This one is for VitalSign data. It gets all data satisfy the condition
	 * from the given timestamp included in the request
	 */
	public List<DetachedData> evaluateFilteringCondition(ProfileData subject, ProfileData resource,
			int cond, String val, boolean latestOnly, int colIdx, int dataId) throws Exception {
		if (FriendService.getFriendRelation(resource.getEmailAddress(), subject.getEmailAddress())==null)
			return null;
		
		List<AbstractData> vals = new ArrayList<AbstractData>();
		
		if (latestOnly)
			vals.add(DataService.getLatestData(resource, dataId));
		else {
			Date latestTimestamp = FriendService.getFriendTimeStamp(resource.getEmailAddress(), subject.getEmailAddress());
			vals = DataService.getData(resource, latestTimestamp, dataId);
		}
		List<DetachedData> returnedVals = new ArrayList<DetachedData>();
		
	
		Date maxTime = null;
		if (vals.size()>0)
			maxTime = vals.get(0).getTimestamp();
		for (AbstractData v : vals) {
			if (v.getTimestamp().after(maxTime))
				maxTime = v.getTimestamp();
			switch (cond) {
			case SimpleFiltering.EQUAL:
				if (v.compareTo(val,colIdx)==0)
					this.addToReturn(v, returnedVals, colIdx);
				break;
			case SimpleFiltering.LEQUAL:
				if (v.compareTo(val,colIdx)==-1)
					this.addToReturn(v, returnedVals,colIdx);
				break;
			case SimpleFiltering.GEQUAL:
				if (v.compareTo(val,colIdx)==1)
					this.addToReturn(v, returnedVals,colIdx);
				break;
			}
		}
		if (vals.size() > 0) {
			
			FriendService.delayTimestampUpdate(resource.getEmailAddress(),
					subject.getEmailAddress(), maxTime);			
		}
		RequestServlet.logger.info("returning data "+returnedVals.size());
		return returnedVals;
	}

	
	private void addToReturn(AbstractData data, List<DetachedData> list, int colIdx) {
		list.add(data.detach(colIdx));
	}
}
